package com.competition.web.admin.config;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;

import javax.annotation.Resource;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import com.common.util.EncryptUtil;
import com.fasterxml.jackson.databind.ObjectMapper;

@Component
public class HttpRequest {

	private RestTemplate restTemplate;

	private URLConfig uRLConfig;

	private ThreadPoolTaskExecutor threadPoolTaskExecutor;

	public ThreadPoolTaskExecutor getThreadPoolTaskExecutor() {
		return threadPoolTaskExecutor;
	}

	@Resource
	public void setRestTemplate(RestTemplate restTemplate) {
		this.restTemplate = restTemplate;
	}

	/**
	 * 按系统时间读取时间戳。如1627038225577
	 * 连接appId、时间戳、请求消息体（以最终实际网络传输为准）、appKey，组成待签名文本。按此例数据，待签名文本应为
	 * ww9c5530210a0d5116_lFERAr1627038225577{"hello":
	 * "tanmarket"}Chey5yRnVGnb7N5Kp9yA9cfU3mTtUuO4 对待签名文本计算 SHA256
	 * 摘要值，以16进制表示，即为最终签名值。本例结果为
	 * 775e156e33f58ea35674de4e9a390ff4298be768a01bb5a998c851f8a770bf9e
	 * 待签名字符计算摘要值时，实际是对待签名字符串的二进制内容进行摘要计算的，请确保此时待签名字符串是使用 UTF-8 编码的。
	 * 对接方可使用此示例数据验证自身签名计算过程正确性。
	 */
	/**
	 * 查询客户字段
	 * 
	 * @param <T>
	 * @param resposeClass
	 * @return
	 */
	public <T> T postProfileFields(ParameterizedTypeReference<T> resposeClass) {

		String param = "{}";
		long stamp = System.currentTimeMillis();

		String appId = this.uRLConfig.getExploringHorsesAppId();

		String appKey = this.uRLConfig.getExploringHorseskey();

		StringBuilder builder = new StringBuilder(appId);

		builder.append(stamp).append(param).append(appKey);

		String sha1 = EncryptUtil.getSHA256(builder.toString());

		HttpHeaders headers = getHttpHeaders(stamp, appId, appKey);
		headers.set("sign", sha1);

		HttpEntity<String> request = new HttpEntity<>(param, headers);
		
		ResponseEntity<T> responseEntity = restTemplate.exchange(this.uRLConfig.getClientColumn(), HttpMethod.POST, request,
				resposeClass);

		if (!responseEntity.getStatusCode().equals(HttpStatus.OK)) {
			return null;
		}

		return responseEntity.getBody();
	}

	private HttpHeaders getHttpHeaders(long stamp, String appId, String appKey) {

		HttpHeaders headers = new HttpHeaders();

		headers.setContentType(MediaType.APPLICATION_JSON);

		headers.set("appId", appId);

		headers.set("appKey", appKey);

		headers.set("timestamp", stamp + "");

		return headers;

	}

	/**
	 * 同步请求 探马客户列表
	 * 
	 * @param <T>
	 * @param <P>
	 * @param param
	 * @param resposeClass
	 * @return
	 */
	public <T, P> T postExploringHorsesSync(P param, ParameterizedTypeReference<T> typeRef) {

		return commonRequestHorsess(param, typeRef, this.uRLConfig.getVisitURL());
	}

	/**
	 * 异步请求搜索探马员工
	 */
	public <T, P> void getStaffByExploringHorsesSync(P param, ParameterizedTypeReference<T> typeRef, Consumer<T> comConsumer) {

		 commonRequestHorsessAsynchronous(param, typeRef, this.uRLConfig.getStaffURL(), comConsumer);

	}

	/**
	 * 同步请求搜索探马员工
	 * 
	 * @param <T>
	 * @param <P>
	 * @param param
	 * @param typeRef
	 * @return
	 */
	public <T, P> T getStaffByExploringHorses(P param, ParameterizedTypeReference<T> typeRef) {

		return commonRequestHorsess(param, typeRef, this.uRLConfig.getStaffURL());
	}

	/**
	 * 异步请求探马转移接口
	 * 
	 * @param <T>
	 * @param <P>
	 * @param param
	 * @param typeRef
	 * @return
	 */
	public <T, P> void transfer(P param, ParameterizedTypeReference<T> typeRef, Consumer<T> comConsumer) {
		commonRequestHorsessAsynchronous(param, typeRef, this.uRLConfig.getTransfer(), comConsumer);
	}
	
	/**
	 * 获取组织架构
	 * 
	 * @param <T>
	 * @param <P>
	 * @return
	 */
	public <T, P> T geOrganize(P param, ParameterizedTypeReference<T> typeRef) {

		return commonRequestHorsess(param, typeRef, this.uRLConfig.getOrganizationalStructureInformation());
	}

	/**
	 * 探马同步公共请求
	 * 
	 * @param <T>
	 * @return
	 */
	private <T, P> T commonRequestHorsess(P param, ParameterizedTypeReference<T> typeRef,
			/* Class<T> resposeClass, */ String url) {

		HttpEntity<P> httpEntity = buildHttpEntity(param);

		if (null == httpEntity) {
			return null;
		}

		ResponseEntity<T> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, typeRef);

//			if (!responseEntity.getStatusCode().equals(HttpStatus.OK)) {
//				return null;
//			}

		return responseEntity.getBody();

	}
	
	
	/**
	 * 探马公共异步请求
	 * 
	 * @param <T>
	 * @return 
	 * @return 
	 * @return
	 */
	private <T, P> void commonRequestHorsessAsynchronous(P param, ParameterizedTypeReference<T> typeRef,
			/* Class<T> resposeClass, */ String url, Consumer<T> consumer) {

		HttpEntity<P> httpEntity = buildHttpEntity(param);

		if (null == httpEntity) {
			return ;
		}
		
		CompletableFuture.supplyAsync(() -> {
			ResponseEntity<T> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, typeRef);
			
			return responseEntity.getBody();
		},this.threadPoolTaskExecutor).thenAccept(consumer).exceptionally(e -> {
			e.printStackTrace();

			return null;
		});


	}
	
	

	private <P> HttpEntity<P> buildHttpEntity(P param) {

		String appId = this.uRLConfig.getExploringHorsesAppId();

		String appKey = this.uRLConfig.getExploringHorseskey();

		long stamp = System.currentTimeMillis();

		HttpHeaders headers = getHttpHeaders(stamp, appId, appKey);
		// 签名

		ObjectMapper objectMapper = new ObjectMapper();

		String json;
		HttpEntity<P> httpEntity = null;
		try {
			json = objectMapper.writeValueAsString(param);
			
//			System.out.println("json: " + json);
			
			StringBuilder builder = new StringBuilder(appId);

			builder.append(stamp).append(json).append(appKey);

			String sha1 = EncryptUtil.getSHA256(builder.toString());

			headers.set("sign", sha1);

			httpEntity = new HttpEntity<>(param, headers);
		} catch (Exception e) {
			e.printStackTrace();// TODO: handle exception
		}

		return httpEntity;
	}

	/**
	 * 获取地址经纬度
	 * 
	 * @param <T>
	 * @param address
	 * @param city
	 * @param targetClass
	 * @param consumer
	 */
	public <T> void get(String address, String city, Class<T> targetClass, Consumer<ResponseEntity<T>> consumer) {

		try {

//			System.out.println("address前: " + address);
			address = address.replaceAll("\\s+", "").replaceAll("\"", "");
			
//			System.out.println("address后: " + address);
			
			String url = this.uRLConfig.getGeoURL();

			
			URI uri = this.getUri(address, city, url);

			System.out.println("uri: " + uri);
			
			CompletableFuture.supplyAsync(() -> {
				
				return restTemplate.getForEntity(uri, targetClass);
			}, threadPoolTaskExecutor).thenAccept(consumer).exceptionally(e -> {

				e.printStackTrace();

				return null;
			});

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * GeoCodes codes = geoCodes.get(0);
	 * 
	 * String location = codes.getLocation();
	 * 
	 * // 统计距离 CompletableFuture<ResponseEntity<Distance>> future =
	 * this.httpRequest.getDistance(staffInfo.getLatitude()+ "," +
	 * staffInfo.getLongitude(), location, Distance.class);
	 * 
	 * @param <T>
	 * @param originLocation 出发点经纬度
	 * @param destination    目的地经纬度
	 * @param target         返回值
	 * @return
	 */
	public <T> CompletableFuture<ResponseEntity<T>> getDistance(String originLocation, String destination,
			Class<T> target) {

		StringBuilder builder = new StringBuilder(this.uRLConfig.getDistance());
		
		String url = builder.append("?key=").append(this.uRLConfig.getKey()).append("&origins=")
				.append(originLocation).append("&destination=").append(destination).toString();
		;

		try {

			return CompletableFuture.supplyAsync(() -> {

				return restTemplate.getForEntity(url, target);
			}, threadPoolTaskExecutor);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;

	}

	/**
	 * 获取路径规划
	 * 
	 * @param <T>
	 * @param originLocation
	 * @param destination
	 * @param city
	 * @param cityd          目的地城市
	 * @param target
	 * @param consumer
	 */
	public <T> void getBusRoutePlanning(String originLocation, String destination, String city, String cityd,
			ParameterizedTypeReference<T> target, Consumer<ResponseEntity<T>> consumer) {

		StringBuilder builder = new StringBuilder(this.uRLConfig.getBusRoutePlanning());
		
		String url = builder.append("?key=").append(this.uRLConfig.getKey())
				.append("&origin=").append(originLocation).append("&destination=").append(destination).append("&city=")
				.append(city).append("&cityd=").append(cityd).append("&time=").append(this.uRLConfig.getDeparture()).toString();
		;
		
		try {
			HttpHeaders httpHeaders = new HttpHeaders();
			HttpEntity<T> entity = new HttpEntity<T>(httpHeaders);

			CompletableFuture.supplyAsync(() -> {
				return restTemplate.exchange(url, HttpMethod.GET, entity, target);
			}).thenAccept(consumer).exceptionally(e -> {
				e.printStackTrace();
				return null;
			});

		} catch (Exception e) {

			e.printStackTrace();
		}
	}
	/**
	 * 步行路径规划
	 * @param <T>
	 */
	public <T> void getWalking(String originLocation, String destination,
			ParameterizedTypeReference<T> target, Consumer<ResponseEntity<T>> consumer) {
		
		String url = this.uRLConfig.getWalking().concat("?key=").concat(this.uRLConfig.getKey())
				.concat("&origin=").concat(originLocation).concat("&destination=").concat(destination)
				;
		
		try {
			HttpHeaders httpHeaders = new HttpHeaders();
			HttpEntity<T> entity = new HttpEntity<T>(httpHeaders);

			CompletableFuture.supplyAsync(() -> {
				return restTemplate.exchange(url, HttpMethod.GET, entity, target);
			}).thenAccept(consumer).exceptionally(e -> {
				e.printStackTrace();
				return null;
			});

		} catch (Exception e) {

			e.printStackTrace();
		}
		
	}

	private URI getUri(String address, String city, String url) throws URISyntaxException {


		url = url.concat("?key=").concat(this.uRLConfig.getKey()).concat("&address=").concat(address).concat("&city=")
				.concat(city);

		return new URI(url);
	}
	
	

	/**
	 * 同步获取经纬度
	 * @param <T>
	 * @param address
	 * @param city
	 * @param targetClass
	 * @return
	 */
	public <T> T getSync(String address, String city, Class<T> targetClass) {

		try {

			String url = this.uRLConfig.getGeoURL();
			
			URI uri = this.getUri(address, city, url);

			ResponseEntity<T> responseEntity = restTemplate.getForEntity(uri, targetClass);

			return responseEntity.getBody();

		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;

	}
	
	/**
	 * 同步获取经纬度
	 * @param <T>
	 * @param address
	 * @param city
	 * @param targetClass
	 * @return
	 */
	public <T> T getGaoDePoi(String address, String city, Class<T> targetClass) {

		try {


			String uri = this.getPoiUri(address, city);

			ResponseEntity<T> responseEntity = restTemplate.getForEntity(uri, targetClass);

			return responseEntity.getBody();

		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;

	}
	
	private String getPoiUri(String address, String city) throws URISyntaxException {

		String url = this.uRLConfig.getPoi();

		return url.concat("?key=")
				.concat(this.uRLConfig.getKey())
				.concat("&keywords=").concat(address).concat("&city=")
				.concat(city)
				.concat("&types=商务住宅|公司企业");

//		return new URI(url);
	}

	@Resource
	public void setGeoConfig(URLConfig uRLConfig) {
		this.uRLConfig = uRLConfig;
	}

	@Resource
	public void setThreadPoolTaskExecutor(ThreadPoolTaskExecutor threadPoolTaskExecutor) {
		this.threadPoolTaskExecutor = threadPoolTaskExecutor;
	}

}